package gui;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import sun.awt.image.BufferedImageGraphicsConfig;

/**
 * JPanel responsible for displaying video
 * @author Gabriel Loewen
 */
public class VideoPane extends JPanel {

    private BufferedImage frame;
    private BufferedImage bi = new BufferedImage(720, 480, BufferedImage.TYPE_INT_RGB);
    private Graphics2D big;
    private long startTime = 0;
    private long curTime = 0;
    private boolean firstTime = true;
    private double fps = 0;
    private int frames = 0;

    public VideoPane() {
        this.setBorder(BorderFactory.createEmptyBorder());
        this.setDoubleBuffered(true);
    }

    public void updateImage(Image img) {
        double ratio = 1.5;
        double width = getHeight() * ratio;

        if (firstTime) {
            startTime = System.currentTimeMillis();
            firstTime = false;
        }

        this.frame = resizeTrick(toBufferedImage(img), (int) width, getHeight());
        bi = (BufferedImage) createImage(this.frame.getWidth(this), this.frame.getHeight(this));

        curTime = System.currentTimeMillis();
        frames++;

        try {
            fps = Math.ceil((frames / ((curTime - startTime) / 1000.0)) * 100) / 100;
            if (fps < 1.0) {
                startTime = System.currentTimeMillis();
                frames = 0;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        repaint();
    }

    public static BufferedImage toBufferedImage(Image image) {

        if (image instanceof BufferedImage) {
            return (BufferedImage) image;
        }

        // This code ensures that all the pixels in the image are loaded
        image = new ImageIcon(image).getImage();

        // Create a buffered image with a format that's compatible with the screen
        BufferedImage bimage = null;

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();

        try {

            // Determine the type of transparency of the new buffered image
            int transparency = Transparency.OPAQUE;

            // Create the buffered image
            GraphicsDevice gs = ge.getDefaultScreenDevice();

            GraphicsConfiguration gc = gs.getDefaultConfiguration();

            bimage = gc.createCompatibleImage(image.getWidth(null), image.getHeight(null), transparency);

        } catch (HeadlessException e) {
        }

        if (bimage == null) {

            // Create a buffered image using the default color model
            int type = BufferedImage.TYPE_INT_RGB;

            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);

        }

        // Copy omage to buffered image
        Graphics g = bimage.createGraphics();
        // Paint the image onto the buffered image

        g.drawImage(image, 0, 0, null);
        g.dispose();

        return bimage;

    }

    public static BufferedImage blurImage(
            Image image) {
        float ninth = 1.0f / 9.0f;
        float[] blurKernel = {
            ninth, ninth, ninth,
            ninth, ninth, ninth,
            ninth, ninth, ninth
        };

        Map map = new HashMap();

        map.put(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

        map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        RenderingHints hints = new RenderingHints(map);
        BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, hints);

        return op.filter((BufferedImage) image, null);
    }

    private static BufferedImage resizeTrick(BufferedImage image, int width, int height) {
        //image = createCompatibleImage(image);
        //image = blurImage(image);
        image = resize(image, width, height);
        return image;
    }

    private static BufferedImage resize(BufferedImage image, int width, int height) {
        int type = image.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : image.getType();
        BufferedImage resizedImage = new BufferedImage(width, height, type);
        Graphics2D g = resizedImage.createGraphics();
        g.setComposite(AlphaComposite.Src);

        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        g.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);

        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        g.drawImage(image, 0, 0, width, height, null);
        g.dispose();
        return resizedImage;
    }

    private static BufferedImage createCompatibleImage(BufferedImage image) {
        GraphicsConfiguration gc = BufferedImageGraphicsConfig.getConfig(image);
        int w = image.getWidth();
        int h = image.getHeight();
        BufferedImage result = gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
        Graphics2D g2 = result.createGraphics();
        g2.drawRenderedImage(image, null);
        g2.dispose();
        return result;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;

        g2.clearRect(0, 0, getWidth(), getHeight());

        g2.setColor(Color.RED);
        if (frame != null && bi != null) {
            big = bi.createGraphics();
            big.drawImage(frame, 0, 0, null);
            g2.drawImage(bi, getWidth() / 2 - bi.getWidth() / 2, getHeight() / 2 - bi.getHeight() / 2, this);
            g2.drawString("FPS: " + fps, getWidth() - g.getFontMetrics().stringWidth("FPS: " + fps) - 10, getHeight() - g.getFontMetrics().getHeight());
        } else {
            g2.drawString("Video feed unavailable.", getWidth() / 2 - g.getFontMetrics().stringWidth("Video feed unavailable.") / 2, getHeight() / 2);
        }
    }
    
}
